BicyclesService.findByLocation   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 5
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 1
rs 10
c 0
b 0
f 0
1 13
import { Injectable, NotFoundException } from '@nestjs/common';
2 13
import { InjectRepository } from '@nestjs/typeorm';
3 13
import { Repository } from 'typeorm';
4 13
import { Bicycle } from './entities/bicycle.entity';
5
import { UpdateBicycleDto } from './dto/update-bicycle.dto';
6
import { BicycleResponse } from './types/bicycle-response.interface';
7 13
import { getDistance } from 'src/utils/geo.utils';
8
import { CreateBicycleDto } from './dto/create-bicycle.dto';
9 13
import { City } from 'src/cities/entities/city.entity';
10 13
import { CityName } from 'src/cities/types/city.enum';
11
import { BicyclePositionDto } from './dto/batch-update.dto';
12
import { BicycleBatchResponse } from './types/BicycleBatchResponse';
13
14
@Injectable()
15 13
export class BicyclesService {
16
  // ai generated code, asked to update previous function which was very inefficient
17
  async updatePositionsParallel(updates: BicyclePositionDto[]): Promise<BicycleBatchResponse[]> {
18 1
    try {
19 1
      const latitudeCases = updates.map((u) => `WHEN id = '${u.id}' THEN ${u.latitude}`).join(' ');
20
21 1
      const longitudeCases = updates
22 1
        .map((u) => `WHEN id = '${u.id}' THEN ${u.longitude}`)
23
        .join(' ');
24
25 1
      await this.bicycleRepository
26
        .createQueryBuilder()
27
        .update()
28
        .set({
29 1
          latitude: () => `CASE ${latitudeCases} ELSE latitude END`,
30 1
          longitude: () => `CASE ${longitudeCases} ELSE longitude END`,
31
        })
32 1
        .whereInIds(updates.map((update) => update.id))
33
        .execute();
34
35 1
      return updates.map((update) => ({
36
        id: update.id,
37
        success: true,
38
      }));
39
    } catch (error) {
40
      return updates.map((update) => ({
41
        id: update.id,
42
        success: false,
43
        error: error.message,
44
      }));
45
    }
46
  }
47
48
  constructor(
49
    @InjectRepository(Bicycle)
50 19
    private readonly bicycleRepository: Repository<Bicycle>,
51
    @InjectRepository(City)
52 19
    private cityRepository: Repository<City>,
53
  ) {}
54
55
  async findAll(): Promise<Bicycle[]> {
56 3
    const bikes = await this.bicycleRepository.find({
57
      relations: {
58
        city: true,
59
      },
60
      select: {
61
        id: true,
62
        batteryLevel: true,
63
        latitude: true,
64
        longitude: true,
65
        status: true,
66
        createdAt: true,
67
        updatedAt: true,
68
        city: {
69
          name: true,
70
        },
71
      },
72
    });
73
74 3
    return bikes;
75
  }
76
77
  async setRented(bikeId: string): Promise<Bicycle> {
78 29
    const result = await this.bicycleRepository.update(
79
      { id: bikeId, status: 'Available' },
80
      { status: 'Rented' },
81
    );
82 29
    if (result.affected === 0) {
83 10
      throw new NotFoundException(
84
        "Bike couldn't be rented. Bike might not exist or it is not available.",
85
      );
86
    }
87 19
    return await this.findById(bikeId);
88
  }
89
90
  async createBike(createBicycleDto: CreateBicycleDto): Promise<Bicycle> {
91 6
    const bike = this.bicycleRepository.create({
92
      batteryLevel: createBicycleDto.batteryLevel ?? 100,
93
      latitude: createBicycleDto.latitude,
94
      longitude: createBicycleDto.longitude,
95
      status: createBicycleDto.status ?? 'Available',
96
    });
97
98 6
    const city = createBicycleDto.city ?? 'Göteborg';
99
    // Find the city by name
100 6
    const cityEntity = await this.cityRepository.findOne({
101
      where: { name: city as CityName },
102
    });
103
104 6
    if (cityEntity) {
105 6
      bike.city = cityEntity;
106
    }
107
108 6
    return await this.bicycleRepository.save(bike);
109
  }
110
111
  async createManyBikes(createBicycleDto: CreateBicycleDto[]): Promise<Bicycle[]> {
112 1
    const defaultCity = await this.cityRepository.findOne({
113
      where: { name: CityName.Göteborg },
114
    });
115 1
    const Karlshamn = await this.cityRepository.findOne({
116
      where: { name: CityName.Karlshamn },
117
    });
118 1
    const Jönköping = await this.cityRepository.findOne({
119
      where: { name: CityName.Jönköping },
120
    });
121
122 1
    const bikes = createBicycleDto.map((bike) => {
123 3
      return this.bicycleRepository.create({
124
        batteryLevel: bike.batteryLevel ?? 100,
125
        latitude: bike.latitude,
126
        longitude: bike.longitude,
127
        status: bike.status ?? 'Available',
128
        city:
129
          bike.city === 'Jönköping'
130
            ? Jönköping
131
            : bike.city === 'Karlshamn'
132
              ? Karlshamn
133
              : defaultCity,
134
      });
135
    });
136
137 1
    return await this.bicycleRepository.save(bikes);
138
  }
139
140
  async findById(id: string): Promise<Bicycle> {
141 30
    const bike = await this.bicycleRepository.findOne({
142
      where: { id },
143
      relations: {
144
        city: true,
145
      },
146
    });
147 30
    if (!bike) {
148 1
      throw new NotFoundException('Bike not found');
149
    }
150 29
    return bike;
151
  }
152
153
  async update(id: string, updateBicycleDto: UpdateBicycleDto): Promise<Bicycle> {
154 6
    const bike = await this.findById(id);
155
156 6
    return this.bicycleRepository.save({ ...bike, ...updateBicycleDto });
157
  }
158
159
  async findByCity(cityName: CityName): Promise<Bicycle[]> {
160 2
    const bikes = await this.bicycleRepository.find({
161
      where: {
162
        city: {
163
          name: cityName,
164
        },
165
      },
166
      relations: ['city'],
167
    });
168
169 2
    return bikes;
170
  }
171
  async findByLocation(lat: number, lon: number, radius: number): Promise<Bicycle[]> {
172 1
    const allBikes = await this.findAll();
173 1
    const filteredBikes = allBikes.filter((bike) => {
174 36
      return getDistance(bike.latitude, bike.longitude, lat, lon) <= radius;
175
    });
176 1
    return filteredBikes;
177
  }
178
  async findByCityAndLocation(
179
    city: any,
180
    lat: number,
181
    lon: number,
182
    radius: number,
183
  ): Promise<Bicycle[]> {
184 2
    const bikesInCity = await this.findByCity(city);
185 2
    const filteredBikes = bikesInCity.filter((bike) => {
186 1
      return getDistance(bike.latitude, bike.longitude, lat, lon) <= radius;
187
    });
188 2
    return filteredBikes;
189
  }
190
191
  private toBicycleResponse(bike: Bicycle): BicycleResponse {
192 60
    return {
193
      id: bike.id,
194
      batteryLevel: bike.batteryLevel,
195
      latitude: bike.latitude,
196
      longitude: bike.longitude,
197
      status: bike.status,
198
      city: bike.city?.name,
199
      createdAt: bike.createdAt,
200
      updatedAt: bike.updatedAt,
201
    };
202
  }
203
204
  toBicycleResponses(bikes: Bicycle[]): BicycleResponse[] {
205 60
    return bikes.map((bike) => this.toBicycleResponse(bike));
206
  }
207
}
208